package keer.httpinterface.http;

import keer.httpinterface.annotation.Arg;
import keer.httpinterface.utils.ClassUtils;
import keer.httpinterface.utils.CollectionUtils;
import keer.httpinterface.utils.StringUtils;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Http参数集合
 *
 * @author Keer
 * @since 1.0.0
 */
public class HttpParams {
    private Map<String, Object[]> params;

    public HttpParams() {
        this(Collections.emptyMap());
    }

    public HttpParams(HttpParams params) {
        this(params.params);
    }

    public HttpParams(Map<String, Object[]> map) {
        this.params = new HashMap<>();
        if (map != null) {
            this.params.putAll(map);
        }
    }
//    public HttpParams addParam(Arg annotation, Object value) {
//        if (value == null) {
//            return this;
//        }
//        if (StringUtils.isBlank(annotation.value())) {
//            if (!ClassUtils.isPrimitiveOrWrapperClass(value.getClass())) {
//                addPojoParameter(value);
//            }
//        } else {
//            addParameter(annotation.value(), value);
//        }
//        return this;
//    }
//
    /**
     * 添加一个pojo对象, 将pojo对象的所有属性作为参数添加到参数集合中，包含父类属性。如果属性标注{@link Arg}注解，
     * 则使用注解指定的名称作为参数名称,否则使用属性名称作为参数名称。
     *
     * @param pojo pojo对象
     */
    public void addPojoParameter(Object pojo) {
        if (pojo == null) {
            return;
        }
        List<Field> fields = ClassUtils.getDeclaredFields(pojo.getClass(), true);
        for (Field f : fields) {
            if (Modifier.isStatic(f.getModifiers()) ||
                    Modifier.isTransient(f.getModifiers())) {
                continue;
            }
            Object value = ClassUtils.getFieldValue(f, pojo);
            String name = f.getName();
            Arg arg = f.getAnnotation(Arg.class);
            if (arg != null && !StringUtils.isBlank(name)) {
                name = arg.value();
            }
            addParameter(name, value);
        }
    }

    /**
     * 添加参数, 如果参数已经存在, 则添加到参数数组中
     *
     * @param key   参数名称
     * @param value 参数值
     */
    public HttpParams addParameter(String key, Object value) {
        if (value == null) {
            return this;
        }
        if (StringUtils.isBlank(key)) {
            if (ClassUtils.isPrimitiveOrWrapper(value)) {
                return this;
            }
        }
        Object[] values = params.get(key);
        if (values == null) {
            values = new Object[0];
        }
        Object[] tmp = new Object[values.length + 1];
        System.arraycopy(values, 0, tmp, 0, values.length);
        tmp[tmp.length - 1] = value;
        params.put(key, tmp);
        return this;
    }
    /**
     * 将当前HttpParams对象与另一个HttpParams对象中的所有参数合并
     * 此方法允许在不改变原对象的情况下，将一组参数添加到当前对象中
     * 如果参数对象为空，则当前对象保持不变
     *
     * @param params 要合并的HttpParams对象，代表一组HTTP参数
     * @return 返回合并后的当前HttpParams对象，以便进行链式调用
     */
    public HttpParams putAll(HttpParams params) {
        // 检查传入的参数对象是否为null，以避免空指针异常
        if (params != null) {
            // 将传入的参数对象中的所有参数合并到当前对象的参数中
            this.params.putAll(params.params);
        }
        // 返回合并后的当前对象，支持链式调用
        return this;
    }

    /**
     * 返回所有参数的映射
     * <p>
     * 该方法将返回一个包含所有参数的Map，其中键是参数名，值是包含该参数所有值的数组
     * 这个方法的存在是因为在某些情况下，我们需要一次性获取所有的请求参数，而不是逐个获取
     * 它提供了一种高效的方式来访问和操作请求参数，特别是在参数数量较多或参数名不确定时
     *
     * @return 包含所有参数名和对应值数组的Map
     */
    public Map<String, Object[]> allParameters() {
        return params;
    }

    /**
     * 根据键获取参数数组
     *
     * @param key 参数的键，用于检索参数数组
     * @return Object[] 返回与键关联的参数数组，如果键不存在，则返回null
     */
    public Object[] getParameters(String key) {
        return params.get(key);
    }
    /**
     * 将参数集合转换为对象，如果参数集合只有一个参数，则返回该参数的值，否则返回一个Map对象，key为参数名称，value为参数值。
     * @return 参数对象
     */
    public Object convertToObject() {
        Map<String, Object[]> map = allParameters();
        if (CollectionUtils.isEmpty(map)) {
            return null;
        }
        if (map.size() == 1) {
            return convertArray(map.values().iterator().next());
        }
        Map<String, Object> m = new LinkedHashMap<>();
        for (String key : map.keySet()) {
            m.put(key, convertArray(map.get(key)));
        }
        return m;
    }

    private Object convertArray(Object[] array) {
        if (CollectionUtils.isEmpty(array)) {
            return null;
        }
        if (array.length == 1) {
            return array[0];
        }
        return array;
    }
    /**
     * 将参数集合转换为查询字符串，并使用指定的字符集进行URL编码
     * @param charset 字符集
     * @return 查询字符串
     */
    public String queryString(String charset) {
        if (params.isEmpty()) {
            return "";
        }
        if (params.size() == 1) {
            Object[] arr = params.values().iterator().next();
            Object value = arr[0];
            if (value != null && !ClassUtils.isPrimitiveOrWrapper(value)) {
                HttpParams tmp = new HttpParams();
                tmp.addPojoParameter(value);
                return tmp.queryString(charset);
            }
        }
        return params.entrySet().stream().map(e -> {
            String ek = StringUtils.urlEncode(e.getKey(), charset);
            Object[] arr = e.getValue();
            if (CollectionUtils.isEmpty(arr)) {
                return ek + "=";
            } else {
                return Arrays.stream(arr).map(o->{
                    StringBuilder builder = new StringBuilder();
                    if (o == null) {
                        builder.append(ek)
                                .append("=");
                    } else {
                        if (o.getClass().isArray()) {
                            int len = Array.getLength(o);
                            if (len > 0) {
                                for (int i = 0; i < len; i++) {
                                    Object ao = Array.get(o, i);
                                    builder.append(ek)
                                            .append("=");
                                    if (ao != null) {
                                        builder.append(StringUtils.urlEncode(ao.toString(), charset));
                                    }
                                    builder.append("&");
                                }
                                builder.setLength(builder.length() - 1);
                            }
                        } else if (Collection.class.isAssignableFrom(o.getClass())) {
                            Collection c = (Collection) o;
                            if (!c.isEmpty()) {
                                for (Object oc : c) {
                                    builder.append(ek)
                                            .append("=");
                                    if (oc != null) {
                                        builder.append(StringUtils.urlEncode(oc.toString(), charset));
                                    }
                                    builder.append("&");
                                }
                                builder.setLength(builder.length() - 1);
                            }
                        } else {
                            builder.append(ek)
                                    .append("=")
                                    .append(StringUtils.urlEncode(o.toString(), charset));
                        }
                    }
                    return builder.toString();
                }).collect(Collectors.joining("&"));
            }
        }).collect(Collectors.joining("&"));
    }
    @Override
    public String toString() {
        return queryString(StandardCharsets.UTF_8.name());
    }
}


